package server;

import interfacePaxos.PaxosRemote;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.List;

public class Acceptor implements Runnable {

  private int my_id;
  private int my_promise;
  private Proposal my_last_accepted_proposal;
  private List<PaxosRemote> my_nodes;
  private boolean alive;
  private static final String PROMISEFILE = "promise.txt";
  private static final String PROPOSALFILE = "proposal.txt";

  public Acceptor(final int the_id, final List<PaxosRemote> the_nodes)
      throws IOException {
    my_id = the_id;
    my_nodes = the_nodes;
    alive = true;

    // Set up promise
    try {
      ObjectInputStream promise_file_input = new ObjectInputStream(
          new FileInputStream(PROMISEFILE));
      my_promise = (int) promise_file_input.readObject();
      promise_file_input.close();
      System.out.println("Read last promise number: " + my_promise);
    } catch (Exception e) {
      // no promise before startup found
      my_promise = 0;
    }

    // Set up last accepted proposal
    try {
      ObjectInputStream proposal_file_input = new ObjectInputStream(
          new FileInputStream(PROPOSALFILE));
      my_last_accepted_proposal = (Proposal) proposal_file_input.readObject();
      proposal_file_input.close();
      System.out.println("Read last proposal: "
          + my_last_accepted_proposal.toString());
    } catch (Exception e) {
      // no proposal before startup found
    }
  }

  /**
   * 
   * @param the_proposal
   * @return
   * @throws IOException
   */
  public void prepare(final Proposal the_proposal) throws IOException {
    System.out.println("Prepare message received: " + the_proposal.toString());
    PaxosRemote remote = my_nodes.get(the_proposal.getProposerId());
    int n = the_proposal.getNumber();
    if (n >= my_promise) {
      my_promise = n;
      FileOutputStream fout_promise = new FileOutputStream(PROMISEFILE);
      ObjectOutputStream my_promise_file = new ObjectOutputStream(fout_promise);
      my_promise_file.writeObject(my_promise);
      my_promise_file.close();
      fout_promise.close();
      remote.promise(my_id, true, null);
    } else {
      remote.promise(my_id, false, my_last_accepted_proposal);
    }
  }

  /**
   * 
   * @param the_proposal
   * @return
   * @throws IOException
   */
  public boolean accept(final Proposal the_proposal) throws IOException {
    boolean result = false;
    if (the_proposal.getNumber() >= my_promise) {
      FileOutputStream fout_proposal = new FileOutputStream(PROPOSALFILE);
      ObjectOutputStream my_proposal_file = new ObjectOutputStream(fout_proposal);
      my_proposal_file.writeObject(the_proposal);
      my_proposal_file.close();
      fout_proposal.close();
      my_last_accepted_proposal = the_proposal;
      result = true;
      for (PaxosRemote remote : my_nodes) {
        remote.notifyLearner(the_proposal);
      }
      System.out.println("Accepted: " + the_proposal.toString());
    } else {
      result = false;
      System.out.println("Rejected: " + the_proposal.toString());
    }
    return result;
  }

  @Override
  public void run() {
    while (alive) {
      Thread.yield();
    }
  }

  public void fail() {
    alive = false;
    System.out.println("Acceptor died.");
  }
}
